    /*  
    championBuilder - Champions Online character planner.
    Copyright (C) 2009 Moritz Hartmeier
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You can find the GNU General Public License in the data folder.
    */


package ch.zarzu.champions.builder.logic;

import java.util.LinkedList;

import ch.zarzu.champions.builder.ErrorLog;

/**
 * This is a Node of a tree which builds itself out of the value given to the root
 * 
 * @author Moritz Hartmeier
 */
public class ValueCalculator {
    LinkedList<ValueCalculator> children;
    CharacteristicObserver char_observer;
    String value;
    
    public ValueCalculator(String v) {
        value = v;
        //System.out.println("new node: " + v);
        char_observer = CharacteristicObserver.getInstance();
        children = new LinkedList<ValueCalculator>();
    }
    
    public Double compute() {
        Double result = 0.0;
        
        try {
            //if it's a simple number, return it as a Double
            if(value.matches("-?[0-9.]+")) {
                result = Double.parseDouble(value);
                
            //evaluates all characteristics, including the level
            } else if(char_observer.hasCharacteristic(value)) {
                result = char_observer.getCharacteristic(value);
            
            } else {
                int count = 0;
                LinkedList<Integer> multiplication = new LinkedList<Integer>();
                LinkedList<Integer> division = new LinkedList<Integer>();
                LinkedList<Integer> addition = new LinkedList<Integer>();
                LinkedList<Integer> subtraction = new LinkedList<Integer>();
                LinkedList<Integer> comma = new LinkedList<Integer>();
                
                char c;
                for(int i = 0; i < value.length(); i++) {
                    c = value.charAt(i);
                    if(c == '(') {
                        count++;
                    } else if(c ==')') {
                        count--;
                    } else if(count == 0) {
                        if(c == '+') {
                            addition.add(i);
                        } else if(c == '-' && i > 0 && value.charAt(i-1) != '*' 
                                                  && value.charAt(i-1) != '/' 
                                                  && value.charAt(i-1) != '+') {
                            subtraction.add(i);
                        } else if(c == '*') {
                            multiplication.add(i);
                        } else if(c == '/') {
                            division.add(i);
                        }
                    //save all commas that occur in tier 1 brackets
                    } else if(count == 1) {
                        if(c == ',')
                            comma.add(i);
                    }
                }
                
                // catch additions
                if(addition.size() > 0) {
                    int last = 0;
                    for(Integer a : addition) {
                        children.add(new ValueCalculator(value.substring(last, a)));
                        last = a + 1;
                    }
                    children.add(new ValueCalculator(value.substring(last)));
                    
                    result = children.getFirst().compute();
                    for(int i = 1; i < children.size(); i++) {
                        result += children.get(i).compute();
                    }
                
                // catch subtractions
                } else if(subtraction.size() > 0) {
                    int last = 0;
                    for(Integer s : subtraction) {
                        children.add(new ValueCalculator(value.substring(last, s)));
                        last = s + 1;
                    }
                    children.add(new ValueCalculator(value.substring(last)));
                    
                    result = children.getFirst().compute();
                    for(int i = 1; i < children.size(); i++) {
                        result -= children.get(i).compute();
                    }
                
                // catch multiplications
                } else if(multiplication.size() > 0) {
                    int last = 0;
                    for(Integer m : multiplication) {
                        children.add(new ValueCalculator(value.substring(last, m)));
                        last = m + 1;
                    }
                    children.add(new ValueCalculator(value.substring(last)));
                    try{
                        result = children.getFirst().compute();
                        for(int i = 1; i < children.size(); i++) {
                            result *= children.get(i).compute();
                        }
                    } catch(Exception e) {
                        
                        e.printStackTrace();
                    }
                
                // catch divisions
                } else if(division.size() > 0) {
                    int last = 0;
                    for(Integer d : division) {
                        children.add(new ValueCalculator(value.substring(last, d)));
                        last = d + 1;
                    }
                    children.add(new ValueCalculator(value.substring(last)));
                    
                    result = children.getFirst().compute();
                    for(int i = 1; i < children.size(); i++) {
                        result /= children.get(i).compute();
                    }
                
                // catch terms that are one big bracket
                } else if(value.startsWith("(") && value.endsWith(")")) {
                    children.add(new ValueCalculator(value.substring(1, value.length() - 1)));
                    result = children.getFirst().compute();
                
                // catch min and max
                } else if(value.startsWith("min(") || value.startsWith("max(")) {
                    int last = 4;
                    for(Integer a : comma) {
                        children.add(new ValueCalculator(value.substring(last, a)));
                        last = a + 1;
                    }
                    children.add(new ValueCalculator(value.substring(last, value.length() - 1)));
                    
                    result = children.getFirst().compute();
                    for(int i = 1; i < children.size(); i++) {
                        if(value.startsWith("min("))
                            result = Math.min(result, children.get(i).compute());
                        else
                            result = Math.max(result, children.get(i).compute());
                    }
                
                // catch square roots
                } else if(value.startsWith("sqrt(")) {
                    children.add(new ValueCalculator(value.substring(5, value.length() - 1)));
                    
                    result = Math.sqrt(children.getFirst().compute());
                
                // catch natural logarithms
                } else if(value.startsWith("ln(")) {
                    children.add(new ValueCalculator(value.substring(3, value.length() - 1)));
                    
                    result = Math.log(children.getFirst().compute());
                
                // catch table entries
                } else if(value.startsWith("table(")) {
                    
                }
            }
        } catch(Exception e) {
            ErrorLog.v("node value: " + value, e);
            e.printStackTrace();
        }
        return result;
    }
}
